using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using JGSY.CMS.LowCode.Platform.Domain.Entities;
using Microsoft.AspNetCore.Authorization;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using System.Collections.Generic;
using System.Linq;
using JGSY.CMS.LowCode.Platform.Infrastructure.Configuration;
using JGSY.CMS.LowCode.Platform.Infrastructure.Configuration.Models;
using JGSY.CMS.LowCode.Platform.Infrastructure.CmsDbContext;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Middleware
{
    /// <summary>
    /// 权限验证中间件
    /// </summary>
    public class PermissionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<PermissionMiddleware> _logger;

    public PermissionMiddleware(RequestDelegate next, ILogger<PermissionMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 跳过认证检查的路径
            if (ShouldSkipPermissionCheck(context.Request.Path))
            {
                await _next(context);
                return;
            }

            // 检查用户是否已认证
            if (!context.User.Identity?.IsAuthenticated == true)
            {
                await _next(context);
                return;
            }

            try
            {
                // 获取端点信息
                var endpoint = context.GetEndpoint();
                if (endpoint == null)
                {
                    await _next(context);
                    return;
                }

                // 检查是否有 AllowAnonymous 特性
                var allowAnonymous = endpoint.Metadata.GetMetadata<AllowAnonymousAttribute>();
                if (allowAnonymous != null)
                {
                    await _next(context);
                    return;
                }

                // 检查是否有 Authorize 特性
                var authorizeAttribute = endpoint.Metadata.GetMetadata<AuthorizeAttribute>();
                if (authorizeAttribute == null)
                {
                    await _next(context);
                    return;
                }

                // 获取用户信息
                var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
                if (string.IsNullOrEmpty(userId))
                {
                    await HandleUnauthorizedAsync(context, "User ID not found in token");
                    return;
                }

                // 检查权限
                var hasPermission = await CheckPermissionAsync(context, userId, authorizeAttribute);
                if (!hasPermission)
                {
                    var (path, method) = (context.Request.Path.Value ?? string.Empty, context.Request.Method);
                    var required = !string.IsNullOrWhiteSpace(authorizeAttribute.Policy)
                        ? authorizeAttribute.Policy
                        : ResolveRequiredPermissionFromPath(context, path, method);
                    
                    await HandleForbiddenAsync(context, $"Insufficient permissions. required={required ?? "<none>"}");
                    
                    // 记录增强的审计日志（简化版本）
                    try
                    {
                        var auditService = context.RequestServices.GetService<IAuditLogAppService>();
                        if (auditService != null)
                        {
                            var ip = context.Connection.RemoteIpAddress?.ToString();
                            var userAgent = context.Request.Headers["User-Agent"].FirstOrDefault();
                            var tenantId = context.Request.Headers["X-Tenant-ID"].FirstOrDefault() ??
                                          context.User.FindFirst("TenantId")?.Value;
                                          
                            var auditDto = new Application.DTOs.AuditLogDto
                            {
                                Type = "PermissionDenied",
                                Content = $"Access denied to {method} {path}. Required permission: {required ?? "unknown"}",
                                UserId = userId,
                                UserName = context.User.Identity?.Name ?? string.Empty,
                                Timestamp = DateTime.UtcNow,
                                IP = ip ?? string.Empty,
                                Result = "Forbidden",
                                Resource = path,
                                TenantId = tenantId,
                                UserAgent = userAgent,
                                CorrelationId = context.TraceIdentifier,
                                HttpMethod = method,
                                RequestPath = path,
                                StatusCode = 403,
                                PermissionCode = required
                            };

                            await auditService.AddAsync(auditDto);
                        }
                    }
                    catch (Exception auditEx)
                    {
                        _logger.LogWarning(auditEx, "Failed to log permission denied event");
                    }
                    
                    return;
                }

                await _next(context);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred during permission check");
                await HandleInternalServerErrorAsync(context, "Permission check failed");
            }
        }

    private async Task<bool> CheckPermissionAsync(HttpContext context, string userId, AuthorizeAttribute authorizeAttribute)
        {
            try
            {
                var permissionService = context.RequestServices.GetRequiredService<IUserPermissionService>();

                // 1) 若 AuthorizeAttribute.Policy 有值，则将其视为权限代码进行检查
                if (!string.IsNullOrWhiteSpace(authorizeAttribute.Policy))
                {
                    var ok = await permissionService.HasPermissionAsync(long.Parse(userId), authorizeAttribute.Policy!);
                    return ok;
                }

                // 2) 否则按资源路径+方法映射表推导所需权限代码
                var required = ResolveRequiredPermissionFromPath(context, context.Request.Path.Value, context.Request.Method);
                if (string.IsNullOrEmpty(required))
                {
                    // 未定义映射则放行
                    return true;
                }

                return await permissionService.HasPermissionAsync(long.Parse(userId), required!);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error checking permission for user {UserId}", userId);
        return false;
            }
        }

    private string? ResolveRequiredPermissionFromPath(HttpContext context, string? path, string method)
        {
            var p = path?.ToLower() ?? string.Empty;
            var m = method.ToUpperInvariant();

            // 从配置读取映射
            var cfgFactory = context.RequestServices.GetService<EnhancedConfigurationMappingFactory>();
            var cfg = cfgFactory?.GetPermissionConfiguration();
            var configMappings = cfg?.RouteMappings ?? new List<JGSY.CMS.LowCode.Platform.Infrastructure.Configuration.Models.PermissionRouteMapping>();

            foreach (var map in configMappings)
            {
                if (IsMatch(p, map.Path))
                {
                    if (map.Methods.TryGetValue(m, out var code) && !string.IsNullOrWhiteSpace(code))
                    {
                        return code;
                    }
                }
            }

            // 可选：启用基于数据库 Resource+Action 的解析（当配置开启时）
            if (cfg?.EnableDatabaseRouteMapping == true)
            {
                try
                {
                    var (resource, action) = GetResourceActionFromPathAndMethod(p, m);
                    if (!string.IsNullOrEmpty(resource) && !string.IsNullOrEmpty(action))
                    {
                        var db = context.RequestServices.GetService<LowCodeDbContext>();
                        if (db != null)
                        {
                            var code = db.Permissions
                                .AsNoTracking()
                                .Where(x => x.IsActive && x.Resource != null && x.Action != null)
                                .Where(x => x.Resource!.ToLower() == resource && x.Action!.ToLower() == action)
                                .Select(x => x.Code)
                                .FirstOrDefault();
                            if (!string.IsNullOrWhiteSpace(code))
                            {
                                return code;
                            }
                        }
                    }
                }
                catch { /* best-effort */ }
            }

            return null;
        }

        private bool IsMatch(string requestPath, string pattern)
        {
            // 支持 ** 跨段前缀匹配，* 单段匹配
            // 将模式转为正则：/api/users/** -> ^/api/users/.*
            // /api/content/*/items -> ^/api/content/[^/]+/items$
            string regex = Regex.Escape(pattern)
                .Replace("\\*\\*", ".*")
                .Replace("\\*", "[^/]+");
            if (!regex.StartsWith("^")) regex = "^" + regex;
            if (!regex.EndsWith("$")) regex += "$";
            return Regex.IsMatch(requestPath, regex, RegexOptions.IgnoreCase);
        }

        private (string resource, string action) GetResourceActionFromPathAndMethod(string path, string method)
        {
            // 约定：/api/{resource}/... -> resource；方法映射：GET->read, POST->create, PUT/PATCH->update, DELETE->delete
            string resource = string.Empty;
            var segments = path.Trim('/').Split('/', StringSplitOptions.RemoveEmptyEntries);
            if (segments.Length >= 2 && segments[0] == "api")
            {
                resource = segments[1].ToLower();
            }

            string action = method switch
            {
                "GET" => "read",
                "POST" => "create",
                "PUT" => "update",
                "PATCH" => "update",
                "DELETE" => "delete",
                _ => string.Empty
            };

            return (resource, action);
        }
        

        private static bool ShouldSkipPermissionCheck(PathString path)
        {
            var skipPaths = new[]
            {
                "/",
                "/api/auth/login",
                "/api/auth/register",
                "/api/auth/refresh-token",
                "/api/auth/reset-password",
                "/api/auth/verify-email",
                "/api/health",
                "/swagger",
                "/favicon.ico"
            };

            return skipPaths.Any(skipPath => 
                path.Value?.StartsWith(skipPath, StringComparison.OrdinalIgnoreCase) == true);
        }

        private async Task HandleUnauthorizedAsync(HttpContext context, string message)
        {
            _logger.LogWarning("Unauthorized access attempt: {Message}. Path: {Path}, IP: {IP}", 
                message, context.Request.Path, context.Connection.RemoteIpAddress);

            context.Response.StatusCode = 401;
            context.Response.ContentType = "application/json";

            var response = new
            {
                error = "Unauthorized",
                message = message,
                timestamp = DateTime.UtcNow,
                path = context.Request.Path.Value
            };

            await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
        }

        private async Task HandleForbiddenAsync(HttpContext context, string message)
        {
            var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            
            _logger.LogWarning("Access forbidden for user {UserId}: {Message}. Path: {Path}", 
                userId, message, context.Request.Path);

            context.Response.StatusCode = 403;
            context.Response.ContentType = "application/json";

            var response = new
            {
                error = "Forbidden",
                message = message,
                timestamp = DateTime.UtcNow,
                path = context.Request.Path.Value
            };

            await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
        }

        private async Task HandleInternalServerErrorAsync(HttpContext context, string message)
        {
            context.Response.StatusCode = 500;
            context.Response.ContentType = "application/json";

            var response = new
            {
                error = "Internal Server Error",
                message = message,
                timestamp = DateTime.UtcNow,
                path = context.Request.Path.Value
            };

            await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
        }

        /// <summary>
        /// 映射审计日志实体到DTO
        /// </summary>
        private static Application.DTOs.AuditLogDto MapAuditLogToDto(Domain.Entities.AuditLog auditLog)
        {
            return new Application.DTOs.AuditLogDto
            {
                Type = auditLog.Type,
                Content = auditLog.Content,
                UserId = auditLog.UserId,
                UserName = auditLog.UserName,
                Timestamp = auditLog.Timestamp,
                IP = auditLog.IP,
                Result = auditLog.Result,
                Exception = auditLog.Exception,
                Resource = auditLog.Resource,
                TenantId = auditLog.TenantId,
                UserAgent = auditLog.UserAgent,
                Duration = auditLog.Duration,
                CorrelationId = auditLog.CorrelationId,
                HttpMethod = auditLog.HttpMethod,
                RequestPath = auditLog.RequestPath,
                StatusCode = auditLog.StatusCode,
                PermissionCode = auditLog.PermissionCode,
                ExtendedData = auditLog.ExtendedData
            };
        }
    }
}
